#pragma once

#ifndef _CORE_PACKAGES_H_
#define _CORE_PACKAGES_H_

#include "Core\SafeSocketStream.h"
#include "Core\Definations.h"

#include "Poco\XML\XMLString.h"
#include "Poco\SAX\Attributes.h"
#include "Poco\SharedPtr.h"
#include "Poco\Net\SocketAddress.h"

#include <map>
#include <fstream>
#include <vector>

namespace ExamSystem
{
    using Poco::XML::XMLString;
    using Poco::XML::XMLChar;
    using Poco::XML::Attributes;
    using Poco::SharedPtr;
    using Poco::Net::SocketAddress;

    // this is the system ID
    extern unsigned int systemID;


    /**************************************************************************\
     Component:     CPackage
     Description:
        this class is a base class for packages. It provides methods for xml
        handler which load information from xml stream and method to write the 
        package to the stream
     \**************************************************************************/
    class CPackage
    {
    public:
        virtual ~CPackage(void) { ; }

    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes) = 0;
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname) = 0;
        virtual void characters(const XMLChar ch[], int start, int length) = 0;

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream) = 0;

        // package type
        virtual const std::string &GetPackageType(void) const = 0;

        // trace string
        virtual std::string ToText(void) const = 0;

    public:
        static const std::string namespaceURI;
        static const std::string labelPackage;
        static const std::string labelExamination;
        static const std::string attributeType;
        static const std::string attributeSID;
        static const std::string capsuledLabelExamination;
        static const std::string capsuledLabelExaminationEnd;

    public:
        unsigned int m_sourseID;
    };

    typedef SharedPtr<CPackage> CPackagePtr;

    template <class PackageType>
    class CPackageBase : public CPackage
    {
        friend class CPackageManager;
    protected:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
        {
        }

        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
        {
        }

        virtual void characters(const XMLChar ch[], int start, int length)
        {
        }


        // package type
        virtual const std::string &GetPackageType(void) const
        {
            return packageType;
        }

        static SharedPtr<CPackage> CreatePackage(void) 
        {
            if (!PackageType::HasMembers())
            {
                static SharedPtr<CPackage> instance = new PackageType;
                return instance;
            }

            return SharedPtr<CPackage>(new PackageType);
        };

    public:
        // this function identifies whether a pack has members
        // for packages which has no members, we will only create its instance once
        inline static bool HasMembers(void) { return true; }

    public:
        static const std::string packageType;
    };

    
    /**************************************************************************\
     Component:     CPackageManager
     Description:
        A manager that create pc
    \**************************************************************************/
    class CPackageManager
    {
    protected:
        CPackageManager(void);
        ~CPackageManager(void);

    public:
        // create a package with given type
        SharedPtr<CPackage> CreatePackage(const std::string &packageType) const;

        // register a package
        typedef SharedPtr<CPackage> (*PackageCreator)(void);
        void RegisterPackage(const std::string &packageType, PackageCreator creator);

        // get the manager instance
        static CPackageManager &Manager(void);

    public:
        std::map<std::string, PackageCreator> m_packageCreator;

        static CPackageManager ms_packageManagerInstance;
    };

#define packageManager CPackageManager::Manager()

    
    /**************************************************************************\
        Package classes
    \**************************************************************************/
    class CKeepAlivePackage : public CPackageBase<CKeepAlivePackage>
    {
    public:   
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        // this function identifies whether a pack has members
        // for packages which has no members, we will only create its instance once
        inline static bool HasMembers(void) { return false; }
    };

    class CKeepAliveReplyPackage : public CPackageBase<CKeepAliveReplyPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        // this function identifies whether a pack has members
        // for packages which has no members, we will only create its instance once
        inline static bool HasMembers(void) { return false; }

    };

    class CIDAllocPackage : public CPackageBase<CIDAllocPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        unsigned int m_ID;
    };

    class CIdentityPackage : public CPackageBase<CIdentityPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum Identity { Server, Client };
        Identity m_identity;
    };

    class CVersionPackage : public CPackageBase<CVersionPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum VersionType { System, Database };
        VersionType m_versionType;
        std::string m_versionString;
    };

    class CResultPackage : public CPackageBase<CResultPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum CheckResult { Passed, Failed };
        CheckResult m_result;
    };

    // this is not a real package, it is just a interface for sending and receiving file
    // note: this class is NOT thread-safe, so use it in a SINGLE thread
    class CFilePackage
    {
    public:
        CFilePackage(void);

    public:
        // receive the file from xml stream
        void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        void characters(const XMLChar ch[], int start, int length);

        // send the file to the stream
        SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // prepare sending 
        bool PrepareSending(void);

        // for display
        virtual std::string ToText(void) const;

    public:
        bool m_flagCorrect;
        std::string m_fileName;

        static std::string ms_receivingPath;

    protected:
        void Reset(void);
        void Decode(void);
        bool CheckFile(void);

    protected:
        bool m_flagReceiving;
        std::fstream m_tempFile;
        std::string m_tempFileName;
        int m_fileSize;
        std::string m_fileMd5;
        std::ifstream m_fin;
    };

    class CDatabasePackage : public CPackageBase<CDatabasePackage>
    {
    public:
        CDatabasePackage(void);

    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        bool IsSucceeded(void) const;
        void SetDatabaseFile(const std::string &fileName);
        const std::string &GetFileName(void) const;

    protected:
        SharedPtr<CFilePackage> m_filePackage;
        bool m_flagReceiving;
    };

    class COrderPackage : public CPackageBase<COrderPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum ExamOrder { StartExam, StopExam };
        ExamOrder m_order;
    };

    class CMessagePackage : public CPackageBase<CMessagePackage>
    {
    public:
        CMessagePackage(void);

        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum MessageType { Information = 0, Alert };
        MessageType m_messageType;
        std::string m_messageBody;

    protected:
        bool m_receiving;
    };

    class CInformationPackage : public CPackageBase<CInformationPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        ExamInformation m_information;

    protected:
        bool m_flagReceivingQuestions;
        QuestionType m_currentType;
        std::string m_receivingString;
    };

    class CLoginPackage : public CPackageBase<CLoginPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        std::string m_username;
        std::string m_password;

    protected:
        std::string m_currentLabel;
    };

    class CLoginResultPackage : public CPackageBase<CLoginResultPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum LoginResult { Error, Succeeded, Failed, NeedInvigilator };
        LoginResult m_result;
    };

    // ask the receiver redirect the connection to the target address
    // the receiver will end the connection
    class CRedirectPackage : public CPackageBase<CRedirectPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        bool m_redirected;
        SocketAddress m_targetAddress;

    protected:
        bool m_receivingAddress;

    };

    // we don't use this package any longer
    // we now use CConnectionInfoPackage
    class CServerInfoPackage : public CPackageBase<CServerInfoPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        Poco::UInt16 m_port;
    };

    // this package contains some information for a connection
    // one package may be passed through many subservers
    class CConnectionInfoPackage : public CPackageBase<CConnectionInfoPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        enum Identity { Client, Server };
        Identity m_sourceIdentity;
        enum Status { Connected, Disconnected };
        Status m_connectionStatus;

        std::vector<int> m_routePath;

        // the following is for client to (re-)login
        std::string m_studentName;
        std::string m_studentNumber;

        // the following is for server
        std::string m_serverHostString;

    protected:
        std::string m_receivingString;
    };

    // question package
    class CQuestionPackage : public CPackageBase<CQuestionPackage>
    {
    public:
        CQuestionPackage(void);

        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

        bool IsSucceeded(void) const;

    public:
        SharedPtr<CQuestion> m_question;

        static const std::string questionTypeName[QuestionTypeCount];

    protected:
        std::string m_currentLabel;
        std::string m_receivedString;
    };

    // question query
    // we send this package only when we want to get questions
    class CQuestionQueryPackage : public CPackageBase<CQuestionQueryPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;
    };

    // criterion package
    class CCriterionPackage : public CPackageBase<CCriterionPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

        bool IsSucceeded(void) const;

    public:
        SharedPtr<CCriterion> m_criterion;
    protected:
        std::string m_currentLabel;
        std::string m_receivedString;
    };
    
    // criterion query
    // we send this package when we want to get criterion
    class CCriterionQueryPackage : public CPackageBase<CCriterionQueryPackage>
    {
    public:
        // stanza handler
        virtual void startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes);
        virtual void endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname);
        virtual void characters(const XMLChar ch[], int start, int length);

        // write package to stream
        virtual SharedPtr<SafeSocketOutputStream> &WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream);

        // for display
        virtual std::string ToText(void) const;

    public:
        std::vector<int> m_questionIds[QuestionTypeCount];

    protected:
        std::string m_currentLabel;
        std::string m_receivingString;
        QuestionType m_currentType;
    };


}


#endif